package com.why.service;

import com.alibaba.fastjson.JSONObject;
import com.why.entity.RedisData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import redis.clients.jedis.PipelineCluster;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * @Description
 * @Author why
 * @Version 1.0.0
 * @Since 1.0
 * @Date 2022/8/18
 */
//@Service
@Slf4j
public class JedisClusterStringService {
    @Autowired
    private PipelineCluster pipelineCluster;

    /**
     * 基于逻辑过期方式解决缓存击穿问题
     *
     * @param prefix
     * @param id
     * @param type
     * @param dbMethod
     * @param time
     * @param timeUnit
     * @param <R>
     * @param <ID>
     * @return
     */
    public <R, ID> R queryWithLogicExpire(String prefix, ID id, Class<R> type, Function<ID, R> dbMethod, Long time, TimeUnit timeUnit) {
        String cacheKey = prefix + id;
        //根据id查询redis中是否有缓存值
        String json = pipelineCluster.get(cacheKey);
        //判断是否存在
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        //命中，把json转化为相应Bean对象
        RedisData redisData = JSONObject.parseObject(json, RedisData.class);
        R r = JSONObject.parseObject(redisData.getValue().toString(), type);
        LocalDateTime logicExpireTime = redisData.getLogicExpireTime();
        //判断是否过期，未过期直接返回信息
        if (logicExpireTime.isAfter(LocalDateTime.now())) {
            return r;
        }
        //已经过期，缓存重建
        String localKey = "lock:" + prefix + id;
        //获取到互斥锁
        if (pipelineCluster.tryLock(localKey, 10L, 5)) {
            CompletableFuture.runAsync(() -> {
                try {
                    //查询数据库
                    R result = dbMethod.apply(id);
                    //写入缓存
                    this.setWithLogicExpire(cacheKey, result, time, timeUnit);
                } catch (Exception e) {

                } finally {
                    //释放互斥锁
                    pipelineCluster.releaseLock(localKey);
                }
            });
        }
        return r;
    }

    /**
     * 设置逻辑过期  kv
     *
     * @param key
     * @param value
     * @param time
     * @param timeUnit
     */
    public void setWithLogicExpire(String key, Object value, Long time, TimeUnit timeUnit) {
        //设置过期时间
        RedisData redisData = new RedisData();
        redisData.setValue(value);
        redisData.setLogicExpireTime(LocalDateTime.now().plusSeconds(timeUnit.toSeconds(time)));
        //写入redis
        pipelineCluster.set(key, JSONObject.toJSONString(redisData));
    }

    public String set(String key, String value) {
        return pipelineCluster.set(key, value);
    }

    public String get(String key) {
        return pipelineCluster.get(key);
    }

    /**
     * api包分布式锁简单实现
     */
    public void tryLock(String key) {
        boolean lock = pipelineCluster.tryLock(key, 1000L, 2);
        try {
            if (lock) {
                log.info("get lock success ");
            }
        } catch (Exception e) {
            log.error("acquire lock fail");
        } finally {
            pipelineCluster.releaseLock("order");
        }
    }

    public Long del(String... key) {
        return pipelineCluster.del(key);
    }

    public String mset() {
//        String[] keysValues = {"k1","v1","k2","v2","k3","v4"};
        // 直接使用pipelineCluster.mset(keysValues) 会直接报错 集群模式对mest命令支持不友好
        Map<String, String> map = new HashMap<>();
        map.put("k1", "v1");
        map.put("k2", "v2");
        map.put("k3", "v3");
        return pipelineCluster.mset(map);
    }

    /**
     * mget: 获取多个kv
     */
    public void mget() {
        Map<String, String> map = pipelineCluster.mget(Arrays.asList("k1", "k2", "k3"));
        log.info("mget command result:{}", map);
    }

    public void incr(String key) {
        Long result = pipelineCluster.incr(key);
        log.info("incr command result:{}", result);
    }

    public void decr(String key) {
        Long result = pipelineCluster.decr(key);
        log.info("decr command result:{}", result);
    }

    /**
     * 同incr，对key的值加increment
     *
     * @param key
     * @param increment
     */
    public void incrby(String key, Long increment) {
        Long result = pipelineCluster.incrBy(key, increment);
        log.info("incrby command result:{}", result);
    }

    /**
     * 同decr，对key的值减decrement
     *
     * @param key
     * @param decrement
     */
    public void decrby(String key, Long decrement) {
        Long result = pipelineCluster.decrBy(key, decrement);
        log.info("decrby command result:{}", result);
    }

    /**
     * 对key的值加浮点型 increment
     *
     * @param key
     * @param increment
     */
    public void incrbyfloat(String key, double increment) {
        Double result = pipelineCluster.incrByFloat(key, increment);
        log.info("decrby command result:{}", result);
    }

    public void append(String key, String appendVal) {
        Long result = pipelineCluster.append(key, appendVal);
        log.info("append command result:{}", result);
    }

    public void strlen(String key) {
        Long result = pipelineCluster.strlen(key);
        log.info("strlen command result:{}", result);
    }

    public void setrange(String key, long offset, String value) {
        Long result = pipelineCluster.setrange(key, offset, value);
        log.info("setrange command result:{}", result);
    }

    public void getrange(String key, long start, long end) {
        String result = pipelineCluster.getrange(key, start, end);
        log.info("getrange command result:{}", result);
    }

    public void getset(String key, String value) {

    }
}
